Skip to content

Conversation

EnzeXing
Copy link
Contributor

When the global object init checker models lazy fields and by-name parameters, initially it creates an environment for each symbol of the lazy field/by-name parameter. For by-name parameters, the environment includes all the possible environments that pass to the parameter as outer environments. This PR modifies the design of environments to take the tree to evaluate as the argument. Therefore, the same of argument tree passed to a by-name parameter will share the same environment, and there will be multiple environments for the same by-name parameter. This facilitates the process of finding local variables in the argument tree. Now the process of evaluating by-name args in multiple-by-name.scala will behave similarly to evaluating closure arguments in multiple-func-arg.scala

(using State.Data, EnvMap.EnvMapMutableData, Trace): EnvRef =
assert((sym.is(Flags.Param) && sym.info.isInstanceOf[ExprType]) || sym.is(Flags.Lazy));
_of(Map.empty, tree, thisV, outerEnv)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is OK, but is there a rationale for the inconsistency that we use the rhs as the tree to identify a lazy val but the overall DefDef as the tree to identify a method (as opposed to the rhs of the method)?

If there is a rationale, it would be good to record it in a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented the rationale. We must use the overall DefDef as the tree to identify a method (as opposed to the rhs of the method) because resolveEnvByMethod looks for the symbol of the DefDef to see it if matches the target method

ref.valValue(target)
else if target.hasSource then
val rhs = target.defTree.asInstanceOf[ValDef].rhs
given Scope = Env.ofByName(target, rhs, ref, Env.NoEnv)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the outerEnv NoEnv? Can't the rhs of a lazy val refer to things from the enclosing scope of its definition site?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For lazy fields, their outer scope is a template and they can refer to other fields. However, currently we do not create environments that encapsulate states of instances, so the outerEnv is NoEnv for lazy fields. The values of other fields can be found by searching through thisV instead, and thisV is an InstanceRef with its fields and outer environments.

Another possible design is to store all outer environments as a chain in EnvMap and bypasses the InstanceRef, or even creates environments for instantiation. Do you prefer any of these designs? Maybe we can modify it in another PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that makes sense. Thanks for the explanation. We can keep the current design.

val byNameEnv = scope match {
case ref: Ref => Env.ofByName(sym, thisV, Env.NoEnv)
case env: Env.EnvRef => Env.ofByName(sym, thisV, Env.EnvSet(Set(env)))
case ref: Ref => Env.ofByName(sym, code, thisV, Env.NoEnv) // for by-name arguments of constructors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: why NoEnv?

case class EnvRef(tree: Tree, owner: ClassSymbol)(using Trace) extends Scope:
def show(using Context) =
"meth: " + meth.show + "\n" +
"tree: " + tree.show + "\n" +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The document for the class EnvRef is outdated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there a problem with equality? The tree will have structural equality, while previously the symbol has reference equality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The document for the class EnvRef is outdated.

Updated the document

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there a problem with equality? The tree will have structural equality, while previously the symbol has reference equality.

Defined the equals method, which checks reference equality of the trees

@EnzeXing EnzeXing force-pushed the fix-global-init-checker-resolve-env branch from 54205cf to 480a6d5 Compare October 1, 2025 18:41
Copy link
Contributor

@liufengyun liufengyun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@olhotak olhotak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now.

@EnzeXing EnzeXing force-pushed the fix-global-init-checker-resolve-env branch from 480a6d5 to 98d2ed5 Compare October 2, 2025 13:14
@olhotak olhotak merged commit cdcc5c1 into scala:main Oct 2, 2025
49 checks passed
@olhotak olhotak deleted the fix-global-init-checker-resolve-env branch October 2, 2025 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants